{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import panel as pn\n",
    "pn.extension()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Discover more on using widgets to add interactivity to your applications in the [how-to guides on interactivity](../../how_to/interactivity/index.md). Alternatively, learn [how to set up callbacks and (JS-)links between parameters](../../how_to/links/index.md) or [how to use them as part of declarative UIs with Param](../../how_to/param/index.md).\n",
    "\n",
    "#### Parameters:\n",
    "\n",
    "For details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n",
    "\n",
    "##### Core\n",
    "\n",
    "* **``options``** (dict | callable): The options to select from. The options may be nested dictionaries, lists, or callables that return those types. If callables are used, the callables must accept `level` and `value` keyword arguments, where `level` is the level that updated and `value` is a dictionary of the current values, containing keys up to the level that was updated.\n",
    "* **``value``** (dict): The value from all the Select widgets; the keys are the levels names. If no levels names are specified, the keys are the levels indices.\n",
    "* **``layout``** (ListPanel | dict): The layout type of the widgets. If a dictionary, a \"type\" key can be provided, to specify the layout type of the widgets, and any additional keyword arguments will be used to instantiate the layout.\n",
    "* **``levels``** (list): Either a list of strings or a list of dictionaries. If a list of strings, the strings are used as the names of the levels. If a list of dictionaries, each dictionary may have a \"name\" key, which is used as the name of the level, a \"type\" key, which is used as the type of widget, and any corresponding widget keyword arguments; otherwise, will inherit layoutable keyword arguments from the `NestedSelect` itself, e.g. width, height, and sizing_mode. Must be specified if options is callable. Must be specified if options is callable.\n",
    "\n",
    "##### Display\n",
    "\n",
    "* **``disabled``** (boolean): Whether the widget is editable\n",
    "* **``name``** (str): The title of the widget\n",
    "\n",
    "___"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "select = pn.widgets.NestedSelect(\n",
    "    options={\n",
    "        \"GFS\": {\n",
    "            \"0.25 deg\": [\"00Z\", \"06Z\", \"12Z\", \"18Z\"],\n",
    "            \"0.5 deg\": [\"00Z\", \"12Z\"],\n",
    "            \"1 deg\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "        \"NAME\": {\n",
    "            \"12 km\": [\"00Z\", \"12Z\"],\n",
    "            \"3 km\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "    },\n",
    "    levels=[\"Model\", \"Resolution\", \"Initialization\"],\n",
    ")\n",
    "select"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Like most other widgets, ``NestedSelect`` has a value parameter that can be accessed or set:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "select.value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A different `layout` type can be provided to the `NestedSelect` to change the layout of the widgets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "select = pn.widgets.NestedSelect(\n",
    "    options={\n",
    "        \"GFS\": {\n",
    "            \"0.25 deg\": [\"00Z\", \"06Z\", \"12Z\", \"18Z\"],\n",
    "            \"0.5 deg\": [\"00Z\", \"12Z\"],\n",
    "            \"1 deg\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "        \"NAME\": {\n",
    "            \"12 km\": [\"00Z\", \"12Z\"],\n",
    "            \"3 km\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "    },\n",
    "    levels=[\"Model\", \"Resolution\", \"Initialization\"],\n",
    "    layout=pn.Row\n",
    ")\n",
    "select"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If a dict is provided, a \"type\" key can be provided, to specify the layout type of the widgets, and any additional keyword arguments will be used to instantiate the layout."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "select = pn.widgets.NestedSelect(\n",
    "    options={\n",
    "        \"GFS\": {\n",
    "            \"0.25 deg\": [\"00Z\", \"06Z\", \"12Z\", \"18Z\"],\n",
    "            \"0.5 deg\": [\"00Z\", \"12Z\"],\n",
    "            \"1 deg\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "        \"NAME\": {\n",
    "            \"12 km\": [\"00Z\", \"12Z\"],\n",
    "            \"3 km\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "    },\n",
    "    levels=[\"Model\", \"Resolution\", \"Initialization\"],\n",
    "    layout={\"type\": pn.GridBox, \"ncols\": 2}\n",
    ")\n",
    "select"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If ``levels`` names are not set, the value is keyed off the level index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "select = pn.widgets.NestedSelect(\n",
    "    options={\n",
    "        \"GFS\": {\n",
    "            \"0.25 deg\": [\"00Z\", \"06Z\", \"12Z\", \"18Z\"],\n",
    "            \"0.5 deg\": [\"00Z\", \"12Z\"],\n",
    "            \"1 deg\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "        \"NAME\": {\n",
    "            \"12 km\": [\"00Z\", \"12Z\"],\n",
    "            \"3 km\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "    },\n",
    ")\n",
    "select.value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also define the default value by providing a dict."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "select = pn.widgets.NestedSelect(\n",
    "    options={\n",
    "        \"GFS\": {\n",
    "            \"0.25 deg\": [\"00Z\", \"06Z\", \"12Z\", \"18Z\"],\n",
    "            \"0.5 deg\": [\"00Z\", \"12Z\"],\n",
    "            \"1 deg\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "        \"NAME\": {\n",
    "            \"12 km\": [\"00Z\", \"12Z\"],\n",
    "            \"3 km\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "    },\n",
    "    value={\"Model\": \"NAME\", \"Resolution\": \"12 km\", \"Initialization\": \"12Z\"},\n",
    "    levels=[\"Model\", \"Resolution\", \"Initialization\"],\n",
    ")\n",
    "select"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Not all keys of the value need to be specified, and the keys can be specified in any order."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "select = pn.widgets.NestedSelect(\n",
    "    options={\n",
    "        \"GFS\": {\n",
    "            \"0.25 deg\": [\"00Z\", \"06Z\", \"12Z\", \"18Z\"],\n",
    "            \"0.5 deg\": [\"00Z\", \"12Z\"],\n",
    "            \"1 deg\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "        \"NAME\": {\n",
    "            \"12 km\": [\"00Z\", \"12Z\"],\n",
    "            \"3 km\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "    },\n",
    "    value={\"Initialization\": \"12Z\", \"Resolution\": \"0.5 deg\"},\n",
    "    levels=[\"Model\", \"Resolution\", \"Initialization\"],\n",
    ")\n",
    "select"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "An incomplete definition of options can also be used. The corresponding subsequent widgets will be hidden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "select = pn.widgets.NestedSelect(\n",
    "    options={\n",
    "        \"NAME\": {},\n",
    "        \"GFS\": {\n",
    "            \"0.25 deg\": [\"00Z\", \"06Z\", \"12Z\", \"18Z\"],\n",
    "            \"0.5 deg\": [\"00Z\", \"12Z\"],\n",
    "            \"1 deg\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "    },\n",
    "    levels=[\"Model\", \"Resolution\", \"Initialization\"],\n",
    ")\n",
    "select"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The value for the hidden widgets will be `None`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "select.value = {\"Model\": \"NAME\"}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternatively, `options` can be a callable. Its nested levels too: e.g. `options={\"Daily\": list_options, \"Monthly\": list_options}`.\n",
    "\n",
    "If callables are used, the callables must accept `level` and `value` keyword arguments, where `level` is the level that updated and `value` is a dictionary of the current values, containing keys up to the level that was updated.\n",
    "\n",
    "Note, the callable can vary across `options`, and `levels` must be provided if any of the `options` is callable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def list_options(level, value):\n",
    "    if level == \"time_step\":\n",
    "        options = {\"Daily\": list_options, \"Monthly\": list_options}\n",
    "    elif level == \"level_type\":\n",
    "        options = {f\"{value['time_step']}_upper\": list_options, f\"{value['time_step']}_lower\": list_options}\n",
    "    else:\n",
    "        options = [f\"{value['level_type']}.json\", f\"{value['level_type']}.csv\"]\n",
    "\n",
    "    return options\n",
    "\n",
    "pn.widgets.NestedSelect(\n",
    "    options=list_options,\n",
    "    levels=[\"time_step\", \"level_type\", \"file_type\"],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is useful if you are trying to use options from a hosted source.\n",
    "\n",
    "Using `pn.cache` here can help improve user experience and reduce the risk of rate limits.\n",
    "\n",
    "```python\n",
    "import panel as pn\n",
    "pn.extension()\n",
    "\n",
    "@pn.cache()\n",
    "def list_options(level, value):\n",
    "    value_path = \"/\".join(list(value.values()))\n",
    "    url = f\"https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis/{value_path}\"\n",
    "\n",
    "    options = [var.rstrip(\"/\") for var in pd.read_html(url)[0][\"Name\"].dropna()[1:]]\n",
    "    if level == \"time_step\":\n",
    "        options = {option: list_options for option in options if option[0].isupper()}\n",
    "    elif level == \"level_type\":\n",
    "        options = {option: list_options for option in options if option[0].islower()}\n",
    "    else:\n",
    "        options = [option for option in options if option.endswith(\".nc\")]\n",
    "\n",
    "    return options\n",
    "\n",
    "\n",
    "select = pn.widgets.NestedSelect(\n",
    "    options=list_options,\n",
    "    levels=[\"time_step\", \"level_type\", \"file\"],\n",
    ")\n",
    "select\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`levels` also accepts a list of dicts, where each dict contains the type of widget and its corresponding kwargs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "select = pn.widgets.NestedSelect(\n",
    "    options={\n",
    "        \"GFS\": {\n",
    "            \"0.25 deg\": [\"00Z\", \"06Z\", \"12Z\", \"18Z\"],\n",
    "            \"0.5 deg\": [\"00Z\", \"12Z\"],\n",
    "            \"1 deg\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "        \"NAME\": {\n",
    "            \"12 km\": [\"00Z\", \"12Z\"],\n",
    "            \"3 km\": [\"00Z\", \"12Z\"],\n",
    "        },\n",
    "    },\n",
    "    value={\"Model\": \"NAME\", \"Resolution\": \"12 km\", \"Initialization\": \"00Z\"},\n",
    "    max_width=500,  # these following kwargs will be inherited by the widgets, unless overridden\n",
    "    sizing_mode=\"stretch_width\",\n",
    "    levels=[\n",
    "        {\"name\": \"Model\", \"type\": pn.widgets.RadioButtonGroup, \"button_type\": \"primary\"},\n",
    "        {\"name\": \"Resolution\", \"type\": pn.widgets.Select, \"max_width\": 300},\n",
    "        {\"name\": \"Initialization\", \"type\": pn.widgets.DiscreteSlider, \"width\": 100, \"sizing_mode\": \"fixed\"},\n",
    "    ],\n",
    ")\n",
    "select"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
